﻿#include "Window.h"

#include "Widgets/WidgetTree.h"
#include "Platform/PlatformInterface.h"
#include "Platform/Window/SystemWindowInterface.h"

#include "Widgets/RootWidget.h"

#include "Widgets/WindowManager.h"

#include "Framework/Application.h"

#include "Render/RenderContext.h"

#include "Render/Widget/RenderSurface.h"
#include "Render/VulkanContext.h"

RWindow::RWindow()
{
    RenderSurface = std::make_shared<CRenderSurface>();
    RenderSurface->SystemWindow= new CSystemWindow();
    RenderSurface->SystemWindow->SetWindowTitle(WindowTitle);


    WidgetTree = new CWidgetTree(this);

    RootWidget = NewObject<RRootWidget>(this);
    RootWidget->SetApplication(GetWindowManager()->GetApplication());
    RootWidget->Register(WidgetTree);
    RootWidget->Width= TDelegate<float>::Create([this]()->float{
        return GetWindowContentSize().X;
    });
    RootWidget->Height= TDelegate<float>::Create([this]()->float{
        return GetWindowContentSize().Y;
    });

    //register to window manager
    GetWindowManager()->RegisterWindow(this);

    Visible=false;
}

RWindow::~RWindow()
{
    GetWindowManager()->UnregisterWindow(this);

    if(WidgetTree)
    {
        delete WidgetTree;
        WidgetTree=nullptr;
    }

}

uint64_t RWindow::GetWindowID()
{
    return RenderSurface->SystemWindow->GetWindowID();
}

SIntPoint RWindow::GetWindowSize()
{
    return RenderSurface->SystemWindow->GetWindowSize();
}

void RWindow::SetWindowSize(SIntPoint InSize)
{
    InSize.X= CMath::Max(InSize.X,0);
    InSize.Y= CMath::Max(InSize.Y,0);

    RenderSurface->SystemWindow->SetWindowSize(InSize);
}

void RWindow::SetWindowSize(int64_t InWidth, int64_t InHeight)
{
    SetWindowSize(SIntPoint(InWidth,InHeight));
}

SIntPoint RWindow::GetWindowPosition()
{
    return RenderSurface->SystemWindow->GetWindowPosition();
}

SIntPoint RWindow::GetWindowContentPosition()
{
    return RenderSurface->SystemWindow->GetContentPosition();
}

SIntPoint RWindow::ContentSpaceToWindowSpace(const SIntPoint &InContentSpacePosition)
{
    auto ContentPosition=GetWindowContentPosition();
    auto WindowPosition=GetWindowPosition();

    return InContentSpacePosition+ContentPosition-WindowPosition;
}

SIntPoint RWindow::GetWindowContentSize()
{
    return RenderSurface->SystemWindow->GetContentSize();
}

void RWindow::SetWindowPosition(SIntPoint InPosition)
{
    SetWindowPosition(InPosition.X,InPosition.Y);
}

void RWindow::SetWindowPosition(int64_t InX, int64_t InY)
{
    RenderSurface->SystemWindow->SetWindowPosition(SIntPoint(InX,InY));
}

void RWindow::MoveToScreenCenter()
{
    RenderSurface->SystemWindow->MoveToScreenCenter();
}

SRect RWindow::GetPlacebleArea()
{
    return RenderSurface->SystemWindow->GetScreenPlacableArea();
}

void RWindow::SetWindowTitle(const CString &InTitle)
{
    WindowTitle=InTitle;
    RenderSurface->SystemWindow->SetWindowTitle(WindowTitle);
}

bool RWindow::IsMaximized() const
{
    return RenderSurface->SystemWindow->IsMaximized();
}

void RWindow::Maximize()
{
    RenderSurface->SystemWindow->Maximize();
}

bool RWindow::IsNormalSizeAndPosition() const
{
    return RenderSurface->SystemWindow->IsNormalSizeAndPosition();
}

void RWindow::Restore()
{
    RenderSurface->SystemWindow->Restore();
}

bool RWindow::IsMinimized() const
{
    return RenderSurface->SystemWindow->IsMinimized();
}

void RWindow::Minimize()
{
    RenderSurface->SystemWindow->Minimize();
}

bool RWindow::IsFullScreen() const
{
    return RenderSurface->SystemWindow->IsFullScreen();
}

void RWindow::FullScreen()
{
    RenderSurface->SystemWindow->FullScreen();
}

void RWindow::ShowSystemBorder()
{
    RenderSurface->SystemWindow->ShowBorder();
}

void RWindow::HideSystemBorder()
{
    RenderSurface->SystemWindow->HideBorder();
}

bool RWindow::IsBorderVisible() const
{
    return RenderSurface->SystemWindow->IsBorderVisible();
}

void RWindow::ClearAllWigets()
{
    RootWidget->ClearSubWidgets();
}

bool RWindow::ProcessWindowEvent(const SWindowEvent &WindowEvent)
{
    switch(WindowEvent.GetType())
    {
        case EWindowEventType::Close:
        {
            Close();
            return true;
        }
        break;

        case EWindowEventType::Resize:
        {
            OnResized.ExecuteIfBound();
            return true;
        }
        break;

        case EWindowEventType::LostFocus:
        {
            bIsFocused=false;
            OnLostFocus.Broadcast(WindowEvent.GetFocusedWindowID());
            return true;
        }

        case EWindowEventType::GotFocus:
        {
            bIsFocused=true;
            OnGotFocus.Broadcast();
            return true;
        }

        default:
          assert(false && "not implemented");
    }

    return false;
}



bool RWindow::ProcessMouseEvent(const SMouseButtonEvent& MouseEvent)
{
    if(AttachedPopWindow && MouseEvent.GetType()==EMouseEventType::ButtonDown)
    {
        if(KeepPopWindowAliveArea.IsBound())
        {
            auto MousePosition=GetMousePosition();
            SRect KeepAliveArea=KeepPopWindowAliveArea.Execute();
            if(!KeepAliveArea.IsPointIn(MousePosition.ToVec2()))
            {
                CloseAttachedPopWindow();
            }
        }
        else
        {
            CloseAttachedPopWindow();
        }
    }

    return GetWidgetTree()->ProcessMouseEvent(MouseEvent);
}

bool RWindow::ProcessTextInput(const STextInputEvent& BoardkeyEvent)
{
    return GetWidgetTree()->ProcessTextInput(BoardkeyEvent);
}

bool RWindow::ProcessKeyboardEvent(const SKeyboardEvent& BoardkeyEvent)
{
    return GetWidgetTree()->ProcessKeyboardEvent(BoardkeyEvent);
}

void RWindow::ExitApplicationOnWindowClosed()
{
    OnWindowClosed.Bind([](){
        RApplication::GetApplication()->Exit();
    });
}

void RWindow::Close()
{
    if(bIsClosed)
    {
        return;
    }


    bIsClosed = true;
    Closed=true;

    //close subwindows
    for(auto SubWindow:SubWindows)
    {
        SubWindow->Close();
    }

    if(ParentWindow && !ParentWindow->IsClosed())
    {
        ParentWindow->SubWindows.Remove(this);     
    }

    OnWindowClosed.ExecuteIfBound();
}

bool RWindow::IsClosed()
{
    return bIsClosed;
}

SRect RWindow::GetScreenPlacableArea()
{
    return RenderSurface->SystemWindow->GetScreenPlacableArea();
}

SIntPoint RWindow::GetMousePosition()
{
    auto RelativePosition=SMouseButtonEvent::GetMosuePositionOnScreen()-GetWindowPosition();
    return RelativePosition;
}

SIntPoint RWindow::GetMousePositionInContent()
{
    return SMouseButtonEvent::GetMosuePositionOnScreen()-GetWindowContentPosition();
}

void RWindow::StartCaptureMouse()
{
    IPlatformInterface::GetPlatformInterface()->CaptureMouse(GetWindowID());
}

void RWindow::StopCaptureMouse()
{
    IPlatformInterface::GetPlatformInterface()->ReleaseMouseCapture();
}

void RWindow::Show()
{
    Visible=true;
    RenderSurface->SystemWindow->Show();
}

void RWindow::Hide()
{
    Visible=false;
    RenderSurface->SystemWindow->Hide();
}

bool RWindow::IsVisible() const
{
    return RenderSurface->SystemWindow->IsVisible();
}

void RWindow::SetOpcity(float InOpacity)
{
    RenderSurface->SystemWindow->SetOpcity(InOpacity);
}

void RWindow::EnableWindow(bool bEnable)
{
    RenderSurface->SystemWindow->EnableThisWindow(bEnable);
}

bool RWindow::IsWindowEnabled() const
{
    return RenderSurface->SystemWindow->IsThisWindowEnabled();
}

void RWindow::BringToFront()
{
    RenderSurface->SystemWindow->BringToFront();
}

void RWindow::HideInTaskbar()
{
    RenderSurface->SystemWindow->HideInTaskbar();
}

void RWindow::NotifyBlinkModalWindow()
{
    OnBlinkModalWindow.ExecuteIfBound();
}

bool RWindow::IsFocused() const
{
    return bIsFocused;
}

void RWindow::Tick(float DeltaSeconds)
{
    bool _Visible=Visible.Get();
    bool _CurrentVisible=RenderSurface->SystemWindow->IsVisible();
    if(_Visible)
    {
        if(!_CurrentVisible)
        {
            RenderSurface->SystemWindow->Show();
        }
    }
    else
    {
        if(_CurrentVisible)
        {
            RenderSurface->SystemWindow->Hide();
        }
    }

    bool _Closed=Closed.Get();
    if(_Closed)
    {
        Close();
        return ;
    }

    if(IsMinimized())
    {
        //minimized window will not update widget tree
        return;
    }

    //update widget tree  grid
    GetWidgetTree()->UpdateWindowGrids(GetWindowContentSize());
    if(_Visible &&!bIsClosed)
    {
        RootWidget->CalculatePresentPositionAndSize();
        RootWidget->Tick(DeltaSeconds);
    }
    GetWidgetTree()->PostHoldGrids();    
}

void RWindow::PaintWidgets()
{
    if(bIsClosed || IsMinimized())
    {
        return;
    }

    RenderSurface->Widgets[RRenderContext::Get()->CurrentWriteBufferIndex].Clear(false);

    bool _Visible=Visible.Get();
    if(!_Visible)
    {
        return;
    }

    //paint
    uint16_t ZOrder=0;
    RootWidget->Paint(ZOrder);

    return;
}

TSharedObjectPtr<RRootWidget> RWindow::GetRootWidget()
{
    return RootWidget;
}

CWidgetTree* RWindow::GetWidgetTree()
{
    return WidgetTree;
}

void RWindow::AttachPopWindow(RWindowPtr InPopWindow,TDelegate<SRect> InKeepAliveArea)
{
    if(AttachedPopWindow)
    {
        AttachedPopWindow->Close();
    }
    AttachedPopWindow=InPopWindow;
    AttachedPopWindow->SetParentWindow(this);
    KeepPopWindowAliveArea=InKeepAliveArea;
}

void RWindow::CloseAttachedPopWindow()
{
    if(AttachedPopWindow)
    {
        AttachedPopWindow->SetParentWindow(nullptr);
        AttachedPopWindow->Close();
        AttachedPopWindow=nullptr;
    }
}

void RWindow::SetParentWindow(RWindowPtr InParentWindow)
{
    if(ParentWindow)
    {
        ParentWindow->SubWindows.Remove(this);
    }

    ParentWindow=InParentWindow;

    if(ParentWindow)
    {
        ParentWindow->SubWindows.Add(this);
    }
}

RWindowPtr RWindow::GetParentWindow()
{
    return ParentWindow;
}

RWindowManager* RWindow::GetWindowManager()
{   
    return RWindowManager::Get();
}